package com.wry.im.server;

import com.wry.im.init.ServerChannelInitializer;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.util.concurrent.GlobalEventExecutor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import javax.annotation.PreDestroy;
import javax.annotation.Resource;
import java.net.InetSocketAddress;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * <h1> 聊天服务器端 <h1>
 *
 * @author wry
 * @since 2023/4/27 13:28
 */
@Component
public class ChatNettyServer {

    // 调度线程组，负责接收请求，不实际处理业务
    EventLoopGroup bossGroup = new NioEventLoopGroup();
    // 工作线程组，负责处理实际业务请求
    EventLoopGroup workGroup = new NioEventLoopGroup();

    private Logger log = LoggerFactory.getLogger(ChatNettyServer.class);

    private static final Integer PORT = 8888;

    public static final Map<String, Channel> USERS = new ConcurrentHashMap<>(1024);

    public static final ChannelGroup GROUP=new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);



    //自定义服务器通道初始化器
    @Resource
    private ServerChannelInitializer serverChannelInitializer;

    public void start() {
        // 定义WebSocket端口
        InetSocketAddress socketAddress = new InetSocketAddress(PORT);

        // 创建主线程
        ServerBootstrap server = new ServerBootstrap();
        server.group(bossGroup, workGroup)
                // 指定通道类型
                .channel(NioServerSocketChannel.class)
                // 指定初始化器
                .childHandler(serverChannelInitializer)
                // 指定初始化端口
                .localAddress(socketAddress)
                // 设置队列大小
                .option(ChannelOption.SO_BACKLOG, 1024)
                // 两小时内没有数据的通信时,TCP会自动发送一个活动探测数据报文
                .childOption(ChannelOption.SO_KEEPALIVE, true);
        try {
            // 绑定端口，接收进来的连接
            final ChannelFuture channelFuture = server.bind(socketAddress).sync();
            log.debug("服务器启动... 开始监听端口：{}",socketAddress.getPort());
            channelFuture.channel().closeFuture().sync();
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            //关闭主线程组
            bossGroup.shutdownGracefully();
            //关闭工作线程组
            workGroup.shutdownGracefully();
        }
    }

    @PreDestroy
    private void destroy() throws InterruptedException {
        bossGroup.shutdownGracefully().sync();
        workGroup.shutdownGracefully().sync();
    }
}
